package be.ac.ulb.mlg.utils;

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2013 Jean-Sebastien Lerat (Jean-Sebastien.Lerat@ulb.ac.be)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * 
 * @author Jean-Sebastien Lerat (Jean-Sebastien.Lerat@ulb.ac.be)
 * @version 1.00, 24/06/2013
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Random;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;

import cern.colt.matrix.DoubleMatrix2D;

public class Measurer{
	/**
	 * Contains the boolean value that specify if the native support is available
	 */
	private static final boolean nativeSupport;
	
	private final static class VirtualRenormalizer implements Renormalizer{
		@Override
		public double[][] normalizeInput(double[][] input) { return input;}
		@Override
		public double[][] normalizeOutput(double[][] input, double[][] output,Measure measure) {return output;}
		@Override
		public boolean hasNativeImplementation() {return false;}
		@Override
		public boolean processInput() {return false;}
		@Override
		public boolean processOutput() {return false;}
		@Override
		public boolean shufflePair() {return false;}
		@Override
		public void simulateRenormalization(double[] shuffledFirst,double[] shuffledSecond, int first, int second) {}
		@Override
		public void updateRenormalization(double[] vect, int index,boolean ignore) {}
	}
	
	static{// initialize the nativeSupport variable
		boolean support = true;
		try{
			final String suffix = System.getProperty("os.arch").contains("64")?"64":"32";
			System.loadLibrary("conet-measure-"+suffix);
			initMeasurer();
		}catch(UnsatisfiedLinkError e){//SecurityException, UnsatisfiedLinkError, NullPointerException
			support = false;
		}
		nativeSupport = support;
	}

	/**
	 * Constant value meaning NO RENORMALIZATION PROCESS
	 */
	public static final Renormalizer NO_RENORMALIZER = null;
	private boolean useNative,useShuffling,shufflePair,preShuffle;
	private int maxMissingValues,nThreads;
	private boolean handleMissingValues;
	private Renormalizer renormalizer;
	private final Random rand = new Random();


	/**
	 * Construct a Measurer trying native, no missing value and no renormalization without any shuffle
	 */
	public Measurer(){
		this(nativeSupport,0,NO_RENORMALIZER);
	}

	private static final native void initMeasurer();

	/**
	 * Construct a Measurer trying native without any shuffle and without renormalization
	 * @param maxMissingValues The number of maximum allowed missing values
	 */
	public Measurer(int maxMissingValues){
		this(nativeSupport,maxMissingValues,NO_RENORMALIZER);
	}
	/**
	 * Construct a Measurer trying native without any shuffle
	 * @param useNative Boolean to try the usage of the native implementation
	 * @param maxMissingValues The number of maximum allowed missing values
	 * @param renormalizer The renormalizer object
	 */
	public Measurer(boolean useNative, int maxMissingValues,Renormalizer renormalizer){
		this.useNative				= useNative;
		this.maxMissingValues		= maxMissingValues;
		this.handleMissingValues	= true;
		this.renormalizer			= renormalizer;
		this.nThreads				= Runtime.getRuntime().availableProcessors();
		this.useShuffling			= this.shufflePair =  this.preShuffle = false;
	}
	/**
	 * Change the usage of shuffle
	 * @param useShuffling new shuffling usage: true for shuffling
	 */
	public void useShuffling(boolean useShuffling){
		this.useShuffling = useShuffling;
	}
	/**
	 * Know the shuffling usage
	 * @return true is shuffle is enabled
	 */
	public boolean isUsingShuffling(){
		return this.useShuffling;
	}
	/**
	 * Define the number of threads to be used
	 * @param nThreads The number of thread must be > 0 (no check)
	 */
	public void setNumberOfThreads(int nThreads){
		this.nThreads=nThreads;
	}
	/**
	 * Know the current number of threads to be used
	 * @return The number of threads
	 */
	public int getNumberOfThreads(){
		return nThreads;
	}
	/**
	 * Retrieve the renormalizer object
	 * @return The renormalizer object
	 */
	public Renormalizer getRenormalizer(){
		return renormalizer;
	}
	/**
	 * Define the renormalizer object
	 * @param renormalizer The new renormalizer object
	 */
	public void setRenormalizer(final Renormalizer renormalizer){
		this.renormalizer = renormalizer;
	}
	/**
	 * Know if the measurer object must take care of missing values
	 * @return true if the measurer check missing values
	 */
	public boolean isHandelingMissingValues(){
		return handleMissingValues;
	}
	/**
	 * Define the checking of missing values
	 * @param handleMissingValues true if the measurer must take care of missing values
	 */
	public void setHandelingMissingValues(final boolean handleMissingValues){
		this.handleMissingValues = handleMissingValues;
	}
	/**
	 * Fill computable array of boolean true iff a OR b contains a missing value
	 * @param computable The boolean array
	 * @param a The first data vector
	 * @param b The second data vector
	 * @return the number of missing values
	 */
	public static final int fillMissing(final boolean[] computable,final double[] a,final double[] b){
		if(computable == null) return 0;
		
		int count=0;
		for(int i=0;i<computable.length;i++){
			computable[i] = !(Double.isNaN(a[i])||Double.isNaN(b[i]));
			if(!computable[i]) count++;
		}
		return count;
	}
	
	/**
	 * Compute the given measures on data without ignored pairs
	 * @param input Input data (stay unchanged)
	 * @param measures The given measures
	 * @return The measured values put into a Mapping: Measure to array (upper triangular)
	 */
	public Map<Measure,double[][]> measure(final DoubleMatrix2D input,final Measure[] measures){
		// Cannot have a proper access to raw data
		return measureImpl(input,measures,null); 
	}
	/**
	 * Compute the given measures on data
	 * @param input Input data (stay unchanged)
	 * @param measures The given measures
	 * @param measurable The boolean array which allow the computation between data pairs data[i] ,data[j] iff measurable[i][j] (eq. measurable[j][i]) is true
	 * @return The measured values put into a Mapping: Measure to array (upper triangular)
	 */
	public Map<Measure,double[][]> measure(final DoubleMatrix2D input,final Measure[] measures,boolean[][] measurable){
		// Cannot have a proper access to raw data
		return measureImpl(input,measures,measurable); 
	}
	/**
	 * Compute the given measures on data without ignored pairs
	 * @param input Input data (stay unchanged)
	 * @param measures The given measures
	 * @return The measured values put into a Mapping: Measure to array (upper triangular)
	 */
	public Map<Measure,double[][]> measure(final double[][] input,final Measure[] measures){
		return measureImpl(input,measures,null);
	}
	/**
	 * Compute the given measures on data
	 * @param input Input data (stay unchanged)
	 * @param measures The given measures
	 * @param measurable  The boolean array which allow the computation between data pairs data[i] ,data[j] iff measurable[i][j] (eq. measurable[j][i]) is true
	 * @return The measured values put into a Mapping: Measure to array (upper triangular)
	 */
	public Map<Measure,double[][]> measure(final double[][] input,final Measure[] measures,boolean[][] measurable){
		return measureImpl(input,measures,measurable);
	}
	/**
	 * Know if the current measurer try to use the native implementation
	 * @return true iff trying to use the native implementation
	 */
	public boolean isUsingNative(){
		return useNative;
	}
	/**
	 * Define the usage of the native implementation
	 * @param useNative true to use the native implementation
	 */
	public void useNative(boolean useNative){
		this.useNative = useNative;
	}
	// The native implementation of the core of Measurer Object
	private native Map<Measure,double[][]> measureNative(final /*double[][]*/DoubleBuffer[] input,final Measure[] measures,final ByteBuffer[] measurable,final int M);
	// The Java implementation of the core of Measurer Object
	private Map<Measure,double[][]> measureJava(final double[][] data,final Measure[] functions, final boolean[][] measurable){
		//initialize
		if(this.renormalizer == NO_RENORMALIZER)
			this.renormalizer = new VirtualRenormalizer();
		final int N = data.length;
		final int M = data[0].length;
		final int MEASURE_COUNT = functions.length;
		int current,measure;
		
		//create/init results arrays, one per measure and configure measures
		final double[][][] results = new double[MEASURE_COUNT][N-1][];
		
		// compute measures as an upper triangular matrix
		final Thread[] threads = new Thread[getNumberOfThreads()];
		{
			final double part		= ((N*(N-1))>>1)/((double)threads.length);
			final double nscale		= N+0.5;
			final boolean pair		= renormalizer!=null?renormalizer.shufflePair():false;
			final boolean shuffling	= (renormalizer!=null && renormalizer.shufflePair()) || isUsingShuffling();
			final int MAX_MISSING	= maxMissingValues;
			
			for(current=0;current<threads.length;current++){
				final int threadID = current;
				threads[current] = new Thread(){
					@Override
					public void run(){
						final int end = threadID >= threads.length-1 ? N-1 :	(int)Math.round(nscale - Math.sqrt(nscale*nscale - 2.0*part*(threadID+2.0)));
						final int start = threadID < 1 ? 0 :					(int)Math.round(nscale - Math.sqrt(nscale*nscale - 2.0*part*(threadID+1.0)));
						int current,ptr,other,measure;
						boolean[] missing = isHandelingMissingValues()?new boolean[M]:null;
						double[] first=null,second=null;
						
						int count = N-start-1;
						final Random rand = new Random();
						if(pair || isUsingShuffling()){
							first	= new double[M];
							second	= new double[M];
						}
						if(measurable != null){
							for(current=start; current<end; current++){
								//create temporary vector measures
								final double[][] currentXother	= new double[MEASURE_COUNT][count];
								
								renormalizer.updateRenormalization(data[current], current, true);
								
								if(!shuffling){
									first = data[current];
								}
								//do the computation
								for(ptr=0,other=current+1; other<N; other++,ptr++){
									if(measurable[current][other]){
										renormalizer.updateRenormalization(data[other], other, true);
										if(shuffling){//pair shuffle
											if(isUsingShuffling()){
												shuffleWithoutReplacement(rand,first,data[current],true);
												shuffleWithoutReplacement(rand,second,data[other],true);
											}
											if(pair){
												/*shuffleWithReplacement*/shuffleWithoutReplacement(rand,first,data[current],true);
												/*shuffleWithReplacement*/shuffleWithoutReplacement(rand,second,data[other],true);
											}
											renormalizer.simulateRenormalization(first,second,current,other);
										}else{
											second = data[other];
										}
										renormalizer.updateRenormalization(data[other], other, false);
										
										if(fillMissing(missing,first,second) > MAX_MISSING){
											//Set forbidden pair to NaN
											for(measure=0;measure<MEASURE_COUNT;measure++)
												currentXother[measure][ptr]=Double.NaN;
										}else{
											//for each measures, compute measure(data[current],data[other],size)
											for(measure=0;measure<MEASURE_COUNT;measure++)
												currentXother[measure][ptr]=functions[measure].measure(first,second,missing);
										}
									}else{
										//Set forbidden pair to NaN
										for(measure=0;measure<MEASURE_COUNT;measure++)
											currentXother[measure][ptr]=Double.NaN;
									}
								}
								//assign current result into the whole result matrix
								for(measure=0;measure<MEASURE_COUNT;measure++)
									results[measure][current] = currentXother[measure];
								//next
								count--;
								renormalizer.updateRenormalization(data[current], current, false);
							}
						} else {
							//SAME WITHOUT ALLOWED CHECKING
							for(current=start; current<end; current++){
								final double[][] currentXother = new double[MEASURE_COUNT][count];
								renormalizer.updateRenormalization(data[current], current, true);
								if(!shuffling){
									first = data[current];
								}
								for(ptr=0,other=current+1; other<N; other++,ptr++){
									renormalizer.updateRenormalization(data[other], other, true);
									if(shuffling){//pair shuffle
										if(isUsingShuffling()){
											shuffleWithoutReplacement(rand,first,data[current],false);
											shuffleWithoutReplacement(rand,second,data[other],false);
										}
										if(pair){
											/*shuffleWithReplacement*/shuffleWithoutReplacement(rand,first,data[current],false);
											/*shuffleWithReplacement*/shuffleWithoutReplacement(rand,second,data[other],false);
										}
										renormalizer.simulateRenormalization(first,second,current,other);
									}else{
										second = data[other];
									}
									renormalizer.updateRenormalization(data[other], other, false);
									
									fillMissing(missing,first,second);
									for(measure=0;measure<MEASURE_COUNT;measure++)
										currentXother[measure][ptr]=functions[measure].measure(first,second,missing);
								}
								for(measure=0;measure<MEASURE_COUNT;measure++)
									results[measure][current] = currentXother[measure];
								count--;
								renormalizer.updateRenormalization(data[current], current, false);
							}
						}
					}
				};
			}
		}
		for(current=0;current<threads.length;current++)
			threads[current].start();
		for(current=0;current<threads.length;current++)
			try {
				threads[current].join();
			} catch (InterruptedException e) {
				//Exception can be caught because it's a fatal error
				e.printStackTrace();
			}
		
		if(this.renormalizer instanceof VirtualRenormalizer)
			this.renormalizer = NO_RENORMALIZER;
		
		//Format result
		final Map<Measure,double[][]> hashMap = new HashMap<Measure,double[][]>();
		for(measure=0;measure<MEASURE_COUNT;measure++){
			if(renormalizer != null){
				results[measure] = renormalizer.normalizeOutput(data, results[measure],functions[measure]);
			}
			hashMap.put(functions[measure],results[measure]);
		}
		
		return hashMap;
	}
	private static void shuffleWithReplacement(Random rand, double[] to, double[] from,final boolean missing) {
		int i;
		
		if(missing){
			int n = 0;
			double[] sampler = new double[to.length];
			
			for(i=0;i<to.length;i++){
				if(!Double.isNaN(from[i])){
					sampler[n++] = from[i];
				}
			}
			for(i=0;i<n;i++){
				to[i] = sampler[rand.nextInt(n)];
			}
			for(;i<to.length;i++){
				to[i] = Double.NaN;
			}
		}else{
			for(i=0;i<to.length;i++){
				to[i] = from[rand.nextInt()];
			}
		}
	}
	private static void shuffleWithoutReplacement(Random rand, double[] to, double[] from,final boolean missing) {
		int i,swap;
		int n = 0;
		double tmp;
		
		if(missing){
			for(i=0;i<from.length;i++){
				if(!Double.isNaN(from[i])){
					to[n++] = from[i];
				}
			}
			for(i=n;i<from.length;i++)
				to[i] = Double.NaN;
		}else{
			n = from.length;
			for(i=0;i<from.length;i++)
				to[i] = from[i];
			
		}
		for(i=0;i<n;i++){
			swap		= rand.nextInt(n);
			tmp			= to[swap];
			to[swap]	= to[i];
			to[i]		= tmp;
		}
	}
	/**
	 * Compute the missing values and return a non null array iff the input contains at least one vector containing more than maxMissingValue
	 * @param input Vector of data vectors
	 * @param measurable Matrix of boolean corresponding of the data vectors set to false if they must be ignorated
	 * @return the new measurable array
	 */
	private boolean[][] checkMissing(final double[][] input,boolean[][] measurable){
		int current,other,pos;
		final int M = input[0].length;
		for(current=0; current<input.length-1; current++){
			for(other=current+1; other<input.length; other++){
				int missing = 0;//for include the maxMissingValues amount
				for(pos=0;pos<M && missing<=maxMissingValues;pos++){
					if(Double.isNaN(input[current][pos]) || Double.isNaN(input[other][pos]))
						missing++;
				}
				if(missing>maxMissingValues){
					if(measurable == null){
						// construct here to preserve the speed up (null) during computation
						measurable = new boolean[input.length][input.length];
						for(boolean[] sub:measurable)
							Arrays.fill(sub,true);
					}
					//Both (diagonal matrix) are not useful but prevent bug for extension of this application
					measurable[current][other]=measurable[other][current]=false;
				}
			}
		}
		
		return measurable;
	}
	//Convert the boolean array to a ByteBuffer (useful for native implementation)
	private static final ByteBuffer[] toByteBuffer(final boolean[][] measurable){
		if(measurable == null) return null;
		
		boolean allocation = true;
		
		final ByteBuffer[] buff = new ByteBuffer[measurable.length];
		final byte YES	= 1;
		final byte NO	= 0;
		int i=0,j;
		for(boolean[] sub:measurable){
			buff[i] = JNIUtils.createByteBuffer(sub.length);
			if(buff[i] == null || (!buff[i].isDirect())) allocation = false;
			for(j=0;j<sub.length;j++)
				buff[i].put(sub[j]?YES:NO);
			i++;
		}
		
		return allocation?buff:null;
	}
	// Check if all required measure are natively implemented
	private static boolean canUseNative(final Measure[] measures){
		for(Measure measure:measures)
			if(!measure.hasNativeImplementation())
				return false;
		return true;
	}
	// Split measure into two vectors. The first contains Measures that read only values and the second one
	// contains Measures that require to modify the data vectors
	private static Measure[][] getTransformers(final Measure[] measures){
		final List<Measure> transform	= new ArrayList<Measure>(measures.length);
		final List<Measure> noTransform	= new ArrayList<Measure>(measures.length);
		for(Measure m:measures)
			if(m.requireDataTransformation()) transform.add(m);
			else noTransform.add(m);
		final Measure[] type = new Measure[0];
		return new Measure[][]{noTransform.toArray(type),transform.toArray(type)};
	}
	//This method prepare data vectors for the Java or native implementation and configure the execution of measurer
	private Map<Measure,double[][]> measureImpl(Object input,final Measure[] measures,boolean[][] measurable){
		final Map<Measure,double[][]> result;
				
		if(useNative && canUseNative(measures)){
			if(this.renormalizer != null && !this.renormalizer.hasNativeImplementation()){
				if(input instanceof DoubleMatrix2D)
					input = ((DoubleMatrix2D)input).toArray();
				if(renormalizer != null)
					input = renormalizer.normalizeInput((double[][])input);
			}
			
			final DoubleBuffer[] buff;
			final ByteBuffer[] computable;
			final int N,M;
			boolean allocation = true;
			try{
				if(input instanceof DoubleMatrix2D){
					//maybe slower but much memory safe than convert through a raw double matrix to a Double buffer and can be contiguous
					final DoubleMatrix2D ref = (DoubleMatrix2D)input;
					N = ref.rows();
					M = ref.columns();
					buff = new DoubleBuffer[N];
					int i,j;
					for(i=0;i<N;i++){
						buff[i] = JNIUtils.createDoubleBuffer(M);
						if(buff[i] == null || (!buff[i].isDirect())) allocation = false;
						//if(isUsingShuffling() && isHandelingMissingValues()){
						//	buff[i].put(missingAtEnd(ref,i));//FIXME remove and see how to preshuffle (remove this process in C)
						//}else{
							for(j=0;j<M;j++)
								buff[i].put(ref.getQuick(i,j));
						//}
					}
				}else{
					final double[][] ref = (double[][])input;
					N	= ref.length;
					M	= ref[0].length;
					buff = new DoubleBuffer[N];
					
					int i=0;
					for(double[] sub:ref){
						buff[i] = JNIUtils.createDoubleBuffer(M);
						if(buff[i] == null || (!buff[i].isDirect())) allocation = false;
						buff[i].put(/*isUsingShuffling() && isHandelingMissingValues()?missingAtEnd(sub):*/sub);//FIXME remove and see how to preshuffle (remove this process in C)
						i++;
					}
				}
				
				computable = toByteBuffer(measurable);
				if(measurable != null && computable == null) allocation = false;
				
				if(!allocation)  throw new OutOfMemoryError("No enought memory to pass data");
			}catch(IllegalArgumentException iae){
				throw new ArrayStoreException("Cannot pass data to native code, it's too huge. Force the use the Java implemelentation.");
			}

			final Measure[][] specifics = getTransformers(measures);
			result = measureNative(buff,specifics[0],computable,M);
			
			double[][] tmpInput;
			if(this.renormalizer != null && this.renormalizer.processOutput()){
				tmpInput = input instanceof double[][]?(double[][])input:JNIUtils.toArray(buff,M);
				for(Measure measure:result.keySet())
					result.put(measure, renormalizer.normalizeOutput(tmpInput, result.get(measure),measure));
			}
			
			useNative(false);
			//Remove threading but memory safe
			result.putAll(measureImpl(input, specifics[1], measurable));
			useNative(true);
			/* Preffer the upper code because it auto process shuffling and preprocessing
			for(Measure measure:specifics[1]){
				//Remove threading but memory safe
				tmpInput = input instanceof double[][]?JNIUtils.toArray((double[][])input):JNIUtils.toArray(buff,M);
				measure.transform(tmpInput);
				result.put(measure, measureJava(tmpInput,new Measure[]{measure},measurable).get(measure));
			}*/
			
		}else{
			// force "raw" data for Java
			if(input instanceof DoubleMatrix2D)
				input = ((DoubleMatrix2D)input).toArray();
			
			//pre-shuffle
			if(preShuffle()){
				double[][] temp = new double[((double[][])input).length][((double[][])input)[0].length];
				for(int row=0;row<((double[][])input).length;row++){
					shuffleWithoutReplacement(this.rand,temp[row],((double[][])input)[row],isHandelingMissingValues());
				}
				
				/*double[][] temp = new double[((double[][])input).length][];
				for(int row=0;row<((double[][])input).length;row++)
					temp[row]=shuffle(Arrays.copyOf(((double[][])input)[row],((double[][])input)[row].length));
				input = temp;*/
			}
			/*if(isUsingShuffling() && isHandelingMissingValues()){// TODO add it in C
				double[][] temp = new double[((double[][])input).length][];
				for(int row=0;row<((double[][])input).length;row++)
					temp[row]=missingAtEnd(((double[][])input)[row]);
				input = temp;
			}*/
			
			if(renormalizer != null)
				input = renormalizer.normalizeInput((double[][])input);
			if(handleMissingValues) measurable = checkMissing((double[][])input,measurable);
			final Measure[][] specifics = getTransformers(measures);
			result = measureJava((double[][])input,specifics[0],measurable);
			for(Measure measure:specifics[1]){
				//Remove threading but memory safe
				final double[][] tmpInput = copy((double[][]) input);
				measure.transform(tmpInput);
				result.put(measure, measureJava(tmpInput,new Measure[]{measure},measurable).get(measure));
			}
		}
		
		if(result != null)return result;
		throw new OutOfMemoryError("Not enought memory, try to use the -Xmx JVM argument.");
	}
/*
	private static double[] missingAtEnd(DoubleMatrix2D matrix,final int row) {//TODO remove
		final double[] nonMissing = new double[matrix.columns()];
		int j = 0;
		for(int column=0;column<nonMissing.length;column++){
			if(!Double.isNaN(matrix.getQuick(row, column))){
				nonMissing[j++] = matrix.getQuick(row, column);
			}
		}
		for(;j<nonMissing.length;j++){
			nonMissing[j] = Double.NaN;
		}
		return nonMissing;
	}

	private static double[] missingAtEnd(double[] data) {//TODO remove
		final double[] nonMissing = new double[data.length];
		int j = 0;
		for(int i=0;i<data.length;i++){
			if(!Double.isNaN(data[i])){
				nonMissing[j++] = data[i];
			}
		}
		for(;j<data.length;j++){
			nonMissing[j] = Double.NaN;
		}
		return nonMissing;
	}*/

	private static double[][] copy(double[][] input) {
		final double[][] cpy = new double[input.length][];
		for(int i=0;i<input.length;i++)
			cpy[i] = Arrays.copyOf(input[i],input[i].length);
		return cpy;
	}
	/**
	 * Enable or disable pre-shuffle of data
	 * @param shuffle true to enable, false to disable
	 */
	public void usePreShuffle(final boolean shuffle){
		this.preShuffle=shuffle;
	}
	/**
	 * Know if the renormalizer use pre-shuffle
	 * @return true if pre-shuffle is enabled, false otherwise
	 */
	public boolean preShuffle(){
		return preShuffle;
	}
	/**
	 * Know the maximum number of missing values
	 * @return The maximum missing values which are allowed
	 */
	public int getMaximumMissingValues(){
		return maxMissingValues;
	}
	/**
	 * Define the number of maximum allowed missing values
	 * @param maxMissingValues The number of missing values
	 */
	public void setMaximumMissingValues(final int maxMissingValues){
		this.maxMissingValues = maxMissingValues;
	}
	/**
	 * Know if the native implementation is available
	 * @return True if the current execution support the native implementation
	 */
	public static boolean hasNativeSupport(){
		return nativeSupport;
	}
} 
